Visual Viewport API
https://gyazo.com/59e5851d06c4e9ad12498fe111eee840
主にiPhone/iPadで、スクリーンキーボードの上にツールバーやステータスバーを実装するのに使う iOSはスクリーンキーボードを開いてもwindow.innerHeightが変化しない
position: fixedで画面の下端にくっつけている要素がキーボードで隠れてしまう
キーボードを背景透過させてwebページがぼけて見える仕様のせい
ボケすぎてて何も見えないけど
iOSでスクリーンキーボードの上、画面下端にツールバーをくっつけたかった
Androidではまったく不要
CSSのposition: fixedとbottom: 0でいい
Androidはスクリーンキーボードを開くとwindow.innerHeightが縮むので
ドキュメント
対応ブラウザ
iOS 13から使える
API
visualViewport.heightやvisualViewport.offsetTop
スクリーンキーボードを除いた大きさと位置が取得できる
window.heightやwindow.offsetTopと同じような感じ
CSSではなくJavaScriptで値を取得し、要素を移動させなければならない
Reactだとこんな感じだが
code:js
const offsetTop = visualViewport.height - 40 + visualViewport.offsetTop;
const style = {
transform: translate(${visualViewport.offsetLeft}px, ${offsetTop}px) scale(${1/visualViewport.scale}) // CSS書いて
}
return (
<div className='bottom-toolbar' style={style}> // styleにセット
画面スクロールなど、適切なイベントを使ってstyleを再セットし続け、ずっとrenderしなおす必要がある
調査した
Androidはすばらしい出来
全てのvisualViewportの値がfloatでリアルタイムに更新される
iOSはひどい
textareaにカーソルを入れ、スクリーンキーボードを表示している間は
スクロールを完全に止めるまで、scrollイベントが発火しなくなる
visualViewport.offsetTopの反映が遅れる
0.5刻みで、Androidより精度が低い
window.scrollYは遅延がない
しかしこちらはIntegerなのでガタガタになる
なぜ1px未満の細かいスクロールができる端末で、開発者が取得できる座標系が整数なんだろう?
Androidはwindow.scrollYもfloatなのに
特にiOS safariでは、上下スクロールによってアドレスバーの高さが変化するのだが
それがvisualViewport.heightに反映されるタイミングが一瞬遅い
スクリーンキーボードを開いていない場合は反応が速い
iPad
Safari Chrome共通
visualViewport.offsetTopの反映が遅い。iOSと同じ
Chrome
スクリーンキーボードの上のパスワードボタンの高さがvisualViewport.heightに含まれていない
これはSafariでは問題ない
結論
こういうのはiOSでは絶対に作れない
スクリーンキーボードの上にツールバーを常に表示し
上下スクロールしてもスクリーンキーボードにぴったりくっついてくるUI
やるならこう
スクロール中は非表示
スクロールが終わったらツールバーを表示する
windowのscrollやresizeイベントを見て、500 msecぐらい静かであったらスクロール完了と見なす
visualViewport.heightとvisualViewport.offsetTopを使って表示位置を計算する
iOSのために作られたAPIだが、iOSはまともに実装されていない
Androidではまったく必要の無いAPIだが、完璧に実装されている